
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>HashMap源码剖析 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>HashMap源码剖析 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">HashMap的默认初始容量、默认负载因子以及链表转换红黑树的阈值分别是多少？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">HashMap的默认初始容量（DEFAULT_INITIAL_CAPACITY）是16，默认负载因子（DEFAULT_LOAD_FACTOR）是0.75f，链表转换为红黑树的阈值（TREEIFY_THRESHOLD）是8。</div>
          </div>
          <div class="card-source">来源: 1.2 核心成员变量</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">为什么HashMap的容量被设计为必须是2的幂？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要有三个原因：1. 提高效率：当容量n是2的幂时，可以通过位运算 `(n - 1) & hash` 来计算索引，这比取模运算 `hash % n` 更高效。2. 减少哈希冲突：容量为2的幂可以保证 `n-1` 的二进制全为1，使得哈希值的每一位都能参与到索引计算中，分布更均匀。3. 方便扩容：扩容时元素的新位置要么在原索引，要么在“原索引+旧容量”，计算简便。</div>
          </div>
          <div class="card-source">来源: 2.3 为什么要用2的幂作为容量</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">算法</div>
          <div class="card-question">HashMap内部的`hash`方法中，为何要将键的hashCode与其高16位进行异或（XOR）操作？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">算法</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">这个操作被称为“扰动函数”。因为在计算桶索引时，通常只使用哈希值的低位。通过将哈希值的高16位与自身进行异或操作，可以让高位的信息也参与到最终的哈希值计算中。这增加了结果的随机性，即使不同键的hashCode低位相同但高位不同，也能有效减少哈希碰撞的概率。</div>
          </div>
          <div class="card-source">来源: 2.1 hash方法解析</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">在JDK 8中，HashMap的哈希冲突解决方案是什么？何时会发生从链表到红黑树的转换？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">JDK 8中，HashMap使用“拉链法”结合红黑树来解决哈希冲突。当一个桶中的链表长度超过阈值TREEIFY_THRESHOLD（默认为8），并且HashMap的总容量大于等于MIN_TREEIFY_CAPACITY（默认为64）时，该链表就会被转换为红黑树以优化查询性能。如果总容量小于64，会优先选择扩容而不是树化。</div>
          </div>
          <div class="card-source">来源: 3.2 JDK 8中的链表+红黑树结构</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">设计</div>
          <div class="card-question">为什么HashMap中红黑树转回链表的阈值（6）要小于链表转红黑树的阈值（8）？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">设计</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">这是为了防止在临界点（例如元素数量在7和8之间波动）时，数据结构在链表和红黑树之间频繁地来回转换。设置一个缓冲区域（8和6之间）可以避免这种因微小变动导致的性能开销。</div>
          </div>
          <div class="card-source">来源: 3.3 红黑树转换阈值分析</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">HashMap的扩容（resize）操作在什么条件下会被触发？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">扩容操作主要在两种情况下触发：1. 当HashMap中的元素数量（size）超过了扩容阈值（threshold），即 `capacity * loadFactor` 时，会在添加新元素后触发扩容。2. 如果使用无参构造函数创建HashMap，那么在第一次添加元素时，会触发初始化并扩容到默认容量16。</div>
          </div>
          <div class="card-source">来源: 4.1 触发条件分析</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">优化</div>
          <div class="card-question">相比JDK 7，JDK 8在HashMap扩容时对元素位置的重计算做了哪些优化？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">优化</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">JDK 8利用了容量是2的幂这一特性进行优化。扩容时，新容量是旧容量的两倍。元素在新表中的位置要么保持不变，要么是“原索引 + 旧容量”。具体位置取决于元素哈希值的一个特定位（`e.hash & oldCap`）。这避免了像JDK 7那样为每个元素重新计算哈希和索引，而是通过一次位运算将一个桶内的元素拆分到两个新桶中，提高了扩容效率。</div>
          </div>
          <div class="card-source">来源: 4.3 JDK 8中的优化：位置重计算</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">实践</div>
          <div class="card-question">为什么强烈建议不要使用可变对象（Mutable Object）作为HashMap的键？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">实践</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">因为HashMap是根据键的`hashCode()`来确定存储位置的。如果一个可变对象在被存入HashMap后，其内部状态发生了改变，导致其`hashCode()`值也随之改变，那么HashMap将无法根据新的哈希值找到原来的存储位置，也无法通过原来的键对象找到它。这会导致数据 фактически丢失，无法被检索到。</div>
          </div>
          <div class="card-source">来源: 5.2 避免可变对象作为键</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">当使用自定义类作为HashMap的键时，`hashCode()`和`equals()`方法必须遵循什么约定？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">必须遵循两个核心约定：1. 如果两个对象通过`equals()`方法比较是相等的，那么它们的`hashCode()`方法必须返回相同的值。2. 如果两个对象的`hashCode()`返回值相同，它们的`equals()`方法不一定返回true（这对应哈希碰撞的情况）。若违反第一条约定，HashMap将无法正常工作。</div>
          </div>
          <div class="card-source">来源: 5.4 hashCode与equals的一致性</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
